/*
 * Copyright 2015-2102 Fast(http://www.cloudate.net) Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.fast.pay.trade.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

import com.fast.pay.account.service.RpAccountTransactionService;
import com.fast.pay.common.core.enums.PublicEnum;
import com.fast.pay.common.core.enums.TradeStatusEnum;
import com.fast.pay.common.core.utils.DateUtils;
import com.fast.pay.notify.service.RpNotifyService;
import com.fast.pay.reconciliation.utils.https.HttpClientUtil;
import com.fast.pay.reconciliation.utils.https.HttpResponse;
import com.fast.pay.trade.dao.RpChannelInfoDao;
import com.fast.pay.trade.dao.RpTradePaymentRecordDao;
import com.fast.pay.trade.entity.RpChannelInfo;
import com.fast.pay.trade.entity.RpTradePaymentRecord;
import com.fast.pay.trade.enums.OrderFromEnum;
import com.fast.pay.trade.enums.TrxTypeEnum;
import com.fast.pay.trade.exception.TradeBizException;
import com.fast.pay.trade.service.RpTradePaymentManagerService;
import com.fast.pay.trade.utils.MerchantApiUtil;
import com.fast.pay.trade.utils.alipay.util.AlipayNotify;
import com.fast.pay.trade.vo.OrderPayResultVo;
import com.fast.pay.trade.vo.ScanPayResultVo;
import com.fast.pay.user.entity.RpUserInfo;
import com.fast.pay.user.entity.RpUserPayConfig;
import com.fast.pay.user.enums.FundInfoTypeEnum;
import com.fast.pay.user.exception.UserBizException;
import com.fast.pay.user.service.BuildNoService;
import com.fast.pay.user.service.RpUserInfoService;
import com.fast.pay.user.service.RpUserPayConfigService;
import com.fast.remote.channel.api.ChannelPayApi;
import com.fast.remote.channel.vo.PayOrderParam;
import com.fast.remote.channel.vo.ResultVo;

/**
 * <b>功能说明:交易模块管理实现类实现</b>
 */
@Service("rpTradePaymentManagerService")
public class RpTradePaymentManagerServiceImpl implements RpTradePaymentManagerService {

    private static final Logger LOG = LoggerFactory.getLogger(RpTradePaymentManagerServiceImpl.class);

    @Autowired
    private RpTradePaymentRecordDao rpTradePaymentRecordDao;

    @Autowired
    private RpUserInfoService rpUserInfoService;


    @Autowired
    private RpUserPayConfigService rpUserPayConfigService;


    @Autowired
    private BuildNoService buildNoService;

    @Autowired
    private RpNotifyService rpNotifyService;

    @Autowired
    private RpAccountTransactionService rpAccountTransactionService;
    
    @Autowired
    private RpChannelInfoDao rpChannelInfoDao;
    
    @Resource(name = "taskExecutor")
    private TaskExecutor taskExecutor;

    
    

    /*@Autowired
    private AliF2FPaySubmit aliF2FPaySubmit;*/

    /**
     * 初始化直连扫码支付数据,直连扫码支付初始化方法规则 1:根据(商户编号 + 商户订单号)确定订单是否存在
     * 1.1:如果订单存在,抛异常,提示订单已存在 1.2:如果订单不存在,创建支付订单 2:创建支付记录 3:根据相应渠道方法
     * 4:调转到相应支付渠道扫码界面
     *
     * @param payKey      商户支付KEY
     * @param productName 产品名称
     * @param orderNo     商户订单号
     * @param orderDate   下单日期
     * @param orderTime   下单时间
     * @param orderPrice  订单金额(元)
     * @param payWayCode  支付方式编码
     * @param orderIp     下单IP
     * @param orderPeriod 订单有效期(分钟)
     * @param returnUrl   支付结果页面通知地址
     * @param notifyUrl   支付结果后台通知地址
     * @param remark      支付备注
     * @param field1      扩展字段1
     * @param field2      扩展字段2
     * @param field3      扩展字段3
     * @param field4      扩展字段4
     * @param field5      扩展字段5
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ScanPayResultVo initDirectScanPay(String payKey, String goodsName, String orderNo, Date orderDate, Date orderTime, BigDecimal orderPrice, RpUserPayConfig rpUserPayConfig, String orderIp, String returnUrl, String notifyUrl, String remark, String field1, String field2, String field3, String field4, String field5) {
 
    	 if (StringUtils.isEmpty(rpUserPayConfig.getChannelCode())) {
             throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付通道未配置");
         }
         
         if (StringUtils.isEmpty(rpUserPayConfig.getRateType())) {
             throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付费率类型未配置");
         }
         
         BigDecimal rateValue = null;
         if (StringUtils.isEmpty(rpUserPayConfig.getRateValue())) {
             throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付费率未配置");
         }
         try {
         	rateValue = new BigDecimal(rpUserPayConfig.getRateValue());
 		} catch (Exception e) {
 			throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付费率有误");
 		}
         
         BigDecimal platIncome = null;
         if(!"RATE".equals(rpUserPayConfig.getRateType()) ) {
             platIncome = orderPrice.multiply(rateValue).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP); // 平台收入

         } else if(!"FIXED".equals(rpUserPayConfig.getRateType())){
        	 platIncome = rateValue;
         }else {
        	 throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付费率类型有误");
         }
        
        //获得支付通道
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        ChannelPayApi bean = null;
        try {
            bean = (ChannelPayApi)wac.getBean(rpUserPayConfig.getChannelCode());
		} catch (Exception e) {
			throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "获得用户支付通道接口失败");
		}
        if(bean==null) {
        	throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付通道未部署");
        }



        String merchantNo = rpUserPayConfig.getUserNo();// 商户编号
        RpUserInfo rpUserInfo = rpUserInfoService.getDataByMerchentNo(merchantNo);
        if (rpUserInfo == null) {
            throw new UserBizException(UserBizException.USER_IS_NULL, "用户不存在");
        }
        
       //查询通道信息表
        RpChannelInfo rpChannelInfo =  rpChannelInfoDao.selectByMerchantNoAndMerchantOrderNo(rpUserPayConfig.getChannelCode(), "PAY");//查询支付通道
        if(rpChannelInfo==null) {
        	throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付通道信息未配置");
        }

        RpTradePaymentRecord rpTradePaymentRecord  = rpTradePaymentRecordDao.getRecordByMerchantNoAndMerchantOrderNo(merchantNo,orderNo);
        if (rpTradePaymentRecord == null) {
             rpTradePaymentRecord = sealRpTradePaymentRecord(merchantNo, rpUserInfo.getUserName(), goodsName, orderNo, orderPrice, rpUserPayConfig.getChannelCode(),
            		 rpUserPayConfig.getProductCode(), platIncome, 
            		 rateValue, orderIp, returnUrl, notifyUrl, remark,  field1, field2, field3, field4, field5);
            rpTradePaymentRecordDao.insert(rpTradePaymentRecord);
        } else {
              throw new TradeBizException(TradeBizException.TRADE_ORDER_ERROR, "订单已存在！");
        }
        
        //请求下游支付通道
        //订单一旦存储不会再有事物回滚，这里要处理所有异常
        ScanPayResultVo scanPayResultVo = new ScanPayResultVo();
        try {
        	 PayOrderParam param = new PayOrderParam();
             try {
     			BeanUtils.copyProperties(param, rpTradePaymentRecord);
     		} catch (Exception e) {
     			LOG.info("参数组装失败："+rpTradePaymentRecord.getMerchantOrderNo(),e); 
     		}
            Map<String, String> ext = new HashMap<String, String>();
            ResultVo res = bean.initPay(param, rpChannelInfo.getKeyStr(), ext);
            scanPayResultVo.setApiFlag(res.getApiFlag());
            scanPayResultVo.setApiMsg(res.getApiMsg());
            scanPayResultVo.setCodeUrl(res.getPayUrl());// 设置微信跳转地址
            scanPayResultVo.setProductName(rpTradePaymentRecord.getProductName());
            scanPayResultVo.setOrderAmount(rpTradePaymentRecord.getOrderAmount());
		} catch (Exception e) {
			LOG.error(rpTradePaymentRecord.getMerchantOrderNo()+"订单请求通道出现未知异常：",e);
			scanPayResultVo.setApiFlag(false);
			scanPayResultVo.setApiMsg("出现未知异常");
		}
        return scanPayResultVo;

    }



    /**
     * 支付成功方法
     *
     * @param rpTradePaymentRecord
     */
    private void completeSuccessOrder(RpTradePaymentRecord rpTradePaymentRecord, String bankReturnMsg) {
        rpTradePaymentRecord.setPaySuccessTime(new Date());
        rpTradePaymentRecord.setBankReturnMsg(bankReturnMsg);
        rpTradePaymentRecord.setStatus(TradeStatusEnum.SUCCESS.name());
        rpTradePaymentRecordDao.update(rpTradePaymentRecord);

        //账户入账
        if (FundInfoTypeEnum.PLAT_RECEIVES.name().equals(rpTradePaymentRecord.getFundIntoType())) {
            rpAccountTransactionService.creditToAccount(rpTradePaymentRecord.getMerchantNo(), rpTradePaymentRecord.getOrderAmount().subtract(rpTradePaymentRecord.getPlatIncome()), rpTradePaymentRecord.getBankOrderNo(), rpTradePaymentRecord.getId(), rpTradePaymentRecord.getTrxType(), rpTradePaymentRecord.getRemark());
        }

        //通知商户
        final String id = rpTradePaymentRecord.getId();
        final String notifyUrl = getMerchantNotifyUrl(rpTradePaymentRecord, rpTradePaymentRecord.getNotifyUrl(),rpTradePaymentRecord.getTrxType(), TradeStatusEnum.SUCCESS);
        taskExecutor.execute(new Runnable() {
			public void run() {
				try {
					notifySend(notifyUrl,id);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
         
    }
    
    
    private void notifySend(String notifyUrl,String id) {
    	try {
    		HttpResponse res = HttpClientUtil.httpsRequest(notifyUrl, "POST", null);
    		InputStream in =res.getInputStream();
    		byte[] bytes = new byte[0];
    		bytes = new byte[in.available()];
    		in.read(bytes);
    		String bank = new String(bytes);
    		if("OK".equals(bank)) {
    			RpTradePaymentRecord rec = rpTradePaymentRecordDao.getById(id);
    			rec.setRemark("已通知");
    			rpTradePaymentRecordDao.update(rec);
    		}
		} catch (IOException e) {
			LOG.error("通知下游失败"+id,e);
		}
    	
    }

    private String getMerchantNotifyUrl(RpTradePaymentRecord rpTradePaymentRecord, String sourceUrl, String productCode,TradeStatusEnum tradeStatusEnum) {

        RpUserPayConfig rpUserPayConfig = rpUserPayConfigService.getByUserNo(rpTradePaymentRecord.getMerchantNo(),productCode);
        if (rpUserPayConfig == null) {
            throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付配置有误");
        }

        Map<String, String> paramMap = new HashMap<>();

        String payKey = rpUserPayConfig.getPayKey();// 企业支付KEY
        paramMap.put("payKey", payKey);
        String productName = rpTradePaymentRecord.getProductName(); // 商品名称
        paramMap.put("productName", productName);
        String orderNo = rpTradePaymentRecord.getMerchantOrderNo(); // 订单编号
        paramMap.put("orderNo", orderNo);
        BigDecimal orderPrice = rpTradePaymentRecord.getOrderAmount(); // 订单金额 ,
        paramMap.put("orderPrice", orderPrice+"");
        String payWayCode = rpTradePaymentRecord.getPayWayCode(); // 支付方式编码 支付宝:
        paramMap.put("payWayCode", payWayCode);
        paramMap.put("tradeStatus", tradeStatusEnum.toString());// 交易状态
        String orderDateStr = DateUtils.formatDate(rpTradePaymentRecord.getCreateTime(), "yyyyMMdd"); // 订单日期
        paramMap.put("orderDate", orderDateStr);
        String remark = rpTradePaymentRecord.getRemark(); // 支付备注
        paramMap.put("remark", remark);
        String trxNo = rpTradePaymentRecord.getTrxNo();// 支付流水号
        paramMap.put("trxNo", trxNo);

        String field1 = rpTradePaymentRecord.getField1(); // 扩展字段1
        paramMap.put("field1", field1);
        String field2 = rpTradePaymentRecord.getField2(); // 扩展字段2
        paramMap.put("field2", field2);
        String field3 = rpTradePaymentRecord.getField3(); // 扩展字段3
        paramMap.put("field3", field3);
        String field4 = rpTradePaymentRecord.getField4(); // 扩展字段4
        paramMap.put("field4", field4);
        String field5 = rpTradePaymentRecord.getField5(); // 扩展字段5
        paramMap.put("field5", field5);

        //通知签名
        String paramStr = MerchantApiUtil.getParamStr(paramMap);
        String sign = MerchantApiUtil.getSign(paramMap, rpUserPayConfig.getPaySecret());
        String notifyUrl = sourceUrl + "?" + paramStr + "&sign=" + sign;

        return notifyUrl;
    }

    /**
     * 支付成功后,又是会出现页面通知早与后台通知 现页面通知,暂时不做数据处理功能,只生成页面通知URL
     * @param payWayCode
     * @param resultMap
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderPayResultVo completeScanPayByResult(String channelCode,String id, Map<String, String> resultMap,String resIoStr,ChannelPayApi bean) {
        OrderPayResultVo orderPayResultVo = new OrderPayResultVo();
        // 根据银行订单号获取支付信息
        RpTradePaymentRecord rpTradePaymentRecord = rpTradePaymentRecordDao.getById(id);
        if (rpTradePaymentRecord == null) {
            throw new TradeBizException(TradeBizException.TRADE_ORDER_ERROR, "非法订单,订单不存在");
        }
        
        if(!TradeStatusEnum.WAITING_PAYMENT.toString().equals(rpTradePaymentRecord.getStatus())) {
        	throw new TradeBizException(TradeBizException.TRADE_ORDER_ERROR, "订单已经结束");
        }
        
        if(!channelCode.equals(rpTradePaymentRecord.getPayWayCode())) {
        	throw new TradeBizException(TradeBizException.TRADE_ORDER_ERROR, "非法通知请求");
        }
        
        //根据通道编码获得通道参数
        RpChannelInfo info = rpChannelInfoDao.selectByMerchantNoAndMerchantOrderNo(channelCode, "PAY");
        if(info==null) {
        	throw new TradeBizException(TradeBizException.TRADE_ORDER_ERROR, "通道参数未设置");
        }
        Map<String, String> extMap = new HashMap<String, String>();
        //访问通道包的异步通知方法
        ResultVo resVo =  bean.syncNotice(resultMap, resIoStr, info.getKeyStr(), extMap);
        TradeStatusEnum orderStatus = resVo.getOrderState();

        // 计算得出通知验证结果
        boolean verify_result = AlipayNotify.verify(resultMap);
        if (verify_result) {// 验证成功
            if (TradeStatusEnum.SUCCESS.equals(orderStatus) ) {
            	completeSuccessOrder(rpTradePaymentRecord, "通知成功");
                orderPayResultVo.setStatus(PublicEnum.YES.name());
            } else {
                orderPayResultVo.setStatus(PublicEnum.NO.name());
            }
        } else {
            throw new TradeBizException(TradeBizException.TRADE_ALIPAY_ERROR, "签名异常");
        }
        return orderPayResultVo;
    }

    /**
     * 封装支付流水记录实体
     *
     * @param merchantNo   商户编号
     * @param merchantName 商户名称
     * @param productName  产品名称
     * @param orderNo      商户订单号
     * @param orderPrice   订单金额
     * @param payWay       支付方式编码
     * @param payWayName   支付方式名称
     * @param payType      支付类型
     * @param fundIntoType 资金流入方向
     * @param feeRate      支付费率
     * @param orderIp      订单IP
     * @param returnUrl    页面通知地址
     * @param notifyUrl    后台通知地址
     * @param remark       备注
     * @param field1       扩展字段1
     * @param field2       扩展字段2
     * @param field3       扩展字段3
     * @param field4       扩展字段4
     * @param field5       扩展字段5
     * @return
     */
    private RpTradePaymentRecord sealRpTradePaymentRecord(String merchantNo, String merchantName, String productName, String orderNo, BigDecimal orderPrice, String channelCode
    		, String productCode, BigDecimal platIncome, BigDecimal feeRate, String orderIp, String returnUrl, String notifyUrl, String remark, String field1, String field2, String field3, String field4, String field5) {
        RpTradePaymentRecord rpTradePaymentRecord = new RpTradePaymentRecord();
        rpTradePaymentRecord.setProductName(productName);// 产品名称
        rpTradePaymentRecord.setMerchantOrderNo(orderNo);// 产品编号

        String trxNo = buildNoService.buildTrxNo();
        rpTradePaymentRecord.setTrxNo(trxNo);// 支付流水号

        String bankOrderNo = buildNoService.buildBankOrderNo();
        rpTradePaymentRecord.setBankOrderNo(bankOrderNo);// 银行订单号
        rpTradePaymentRecord.setMerchantName(merchantName);
        rpTradePaymentRecord.setMerchantNo(merchantNo);// 商户编号
        rpTradePaymentRecord.setOrderIp(orderIp);// 下单IP
        rpTradePaymentRecord.setOrderRefererUrl("");// 下单前页面
        rpTradePaymentRecord.setReturnUrl(returnUrl);// 页面通知地址
        rpTradePaymentRecord.setNotifyUrl(notifyUrl);// 后台通知地址
        rpTradePaymentRecord.setPayWayCode(channelCode);// 支付通道编码
        //rpTradePaymentRecord.setPayWayName(payWayName);// 支付通道名称
        rpTradePaymentRecord.setTrxType(TrxTypeEnum.EXPENSE.name());// 交易类型
        rpTradePaymentRecord.setOrderFrom(OrderFromEnum.USER_EXPENSE.name());// 订单来源
        rpTradePaymentRecord.setOrderAmount(orderPrice);// 订单金额
        rpTradePaymentRecord.setStatus(TradeStatusEnum.WAITING_PAYMENT.name());// 订单状态
        // 等待支付

        rpTradePaymentRecord.setPayTypeCode(productCode);// 支付类型
        //rpTradePaymentRecord.setPayTypeName(payType.getDesc());// 支付方式
        //rpTradePaymentRecord.setFundIntoType(fundIntoType);// 资金流入方向

        rpTradePaymentRecord.setFeeRate(feeRate);// 费率
        //rpTradePaymentRecord.setPlatCost(platCost);// 平台成本
        rpTradePaymentRecord.setPlatIncome(platIncome);// 平台收入
        //rpTradePaymentRecord.setPlatProfit(platProfit);// 平台利润

        rpTradePaymentRecord.setRemark(remark);// 支付备注
        rpTradePaymentRecord.setField1(field1);// 扩展字段1
        rpTradePaymentRecord.setField2(field2);// 扩展字段2
        rpTradePaymentRecord.setField3(field3);// 扩展字段3
        rpTradePaymentRecord.setField4(field4);// 扩展字段4
        rpTradePaymentRecord.setField5(field5);// 扩展字段5
        return rpTradePaymentRecord;
    }



	@Override
	public ChannelPayApi getChennelBean(String channelCode) {
		//获得支付通道
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        ChannelPayApi bean = null;
        try {
            bean = (ChannelPayApi)wac.getBean(channelCode);
		} catch (Exception e) {
			throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "获得用户支付通道接口失败");
		}
        if(bean==null) {
        	throw new UserBizException(UserBizException.USER_PAY_CONFIG_ERRPR, "用户支付通道未部署");
        }
		return bean;
	}



	@Override
	public void completeScanPayByQuery(String id) {
        // 根据银行订单号获取支付信息
        RpTradePaymentRecord rpTradePaymentRecord = rpTradePaymentRecordDao.getById(id);
        if (rpTradePaymentRecord == null) {
            throw new TradeBizException(TradeBizException.TRADE_ORDER_ERROR, "非法订单,订单不存在");
        }
        
        if(!TradeStatusEnum.WAITING_PAYMENT.toString().equals(rpTradePaymentRecord.getStatus())) {
        	throw new TradeBizException(TradeBizException.TRADE_ORDER_ERROR, "订单已经结束");
        }
        completeSuccessOrder(rpTradePaymentRecord, "同步成功");
	}
    
}
